#pragma once
#include <iostream>
#include <fstream>
#include <filesystem>
#include <memory>
#include <sstream>
#include <unistd.h>
#include "mutex.hpp"
using namespace mymutex;

namespace mylog
{
    const std::string gsep = "\r\n";
    // 刷新策略
    class logflush
    {
    public:
        virtual void flush(std::string msg) = 0;
        // ~logflush() = delete;
    };
    class displayflush : public logflush
    {
    public:
        void flush(std::string msg) override
        {
            LockGroup ldp(_mutex);
            std::cout << msg << std::endl;
        }

    private:
        Mutex _mutex;
    };
    static std::string default_path = "./log";
    static std::string default_name = "log.log";
    class fileflush : public logflush
    {
    public:
        fileflush(const std::string &path = default_path, const std::string &name = default_name)
            : _path(path), _name(name)
        {
            LockGroup ldp(_mutex);
            if (std::filesystem::exists(_path))
            {
                // 路径存在
                return;
            }
            // 路径不存在，创建
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << gsep;
            }
        }
        void flush(std::string msg) override
        {
            LockGroup lgp(_mutex);
            _pathname = _path + (_path.back() == '/' ? "" : "/") + _name; // 生成文件路径+名称
            std::ofstream out(_pathname, std::ios::app);                  // 以追加方式打开文件
            if (!out.is_open())
            {
                // 打开文件失败
                return;
            }
            // 输出
            out << msg << gsep;
            out.close();
        }

    private:
        std::string _path;
        std::string _name;
        std::string _pathname;
        Mutex _mutex;
    };
    std::string GetTime()
    {
        time_t tm = time(nullptr);
        struct tm curr;
        localtime_r(&tm, &curr);
        std::stringstream ss;
        ss << curr.tm_year + 1900 << "-"
           << curr.tm_mon + 1 << "-"
           << curr.tm_mday << " "
           << curr.tm_hour << ":"
           << curr.tm_min << ":"
           << curr.tm_sec;
        return ss.str();
    }
    enum class Level
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    std::string GetLevel(Level level)
    {
        switch (level)
        {
        case Level::DEBUG:
            return "DEBUG";
        case Level::INFO:
            return "INFO";
        case Level::WARNING:
            return "WARNING";
        case Level::ERROR:
            return "ERROR";
        case Level::FATAL:
            return "FATAL";
        default:
            return "UNKONW";
        }
    }
    // 日志内容
    class Log
    {
    public:
        Log()
        {
            _logflush = std::make_unique<displayflush>();
        }
        void EnableDisplay()
        {
            _logflush = std::make_unique<displayflush>();
        }
        void EnableFile()
        {
            _logflush = std::make_unique<fileflush>();
        }
        // 一条日志信息
        class Logmassage
        {
        public:
            Logmassage(Level level, std::string filename, int line, Log *log)
                : _time(GetTime()),
                  _level(level),
                  _pid(getpid()),
                  _filename(filename),
                  _line(line),
                  _log(log)
            {
                std::stringstream ss;
                ss << '[' << _time << "] "
                   << '[' << GetLevel(_level) << "] "
                   << '[' << _pid << "] "
                   << '[' << _filename << "] "
                   << '[' << _line << "] ";
                _logmassage = ss.str();
            }
            ~Logmassage()
            {
                if (_log->_logflush)
                {
                    // 刷新日志
                    _log->_logflush->flush(_logmassage);
                }
            }
            template <class T>
            Logmassage &operator<<(const T &data)
            {
                std::stringstream ss;
                ss << data;
                _logmassage += ss.str();
                return *this;
            }

        private:
            std::string _time; // 时间
            Level _level;      // 日志等级
            pid_t _pid;
            std::string _filename;   // 文件名
            int _line;               // 行号
            std::string _massage;    // 信息
            std::string _logmassage; // 完整的日志信息
            Log *_log;               // Log 指针
        };
        Logmassage operator()(Level level, std::string filename, int line)
        {
            return Logmassage(level, filename, line, this);
        }

    private:
        std::unique_ptr<logflush> _logflush;
    };
    Log log;
#define LOG(level) log(level, __FILE__, __LINE__)
#define ENABLEDISPLAY() log.EnableDisplay()
#define ENABLEFILE() log.EnableFile()
}